#include <string>
#include <vector>
#include <cstdio>

#include <Windows.h>

#include "corona.h"

#include "../../source/TexturePacker.hpp"
#include "../../source/tinyxml/tinyxml.h"

void GetFileNames(const std::string &path, std::vector<std::string> &names);

struct ImageData
{
	corona::Image *image;
	float originX, originY;
	std::string name;
};

int main(int argc, char *argv[])
{
	char *usage = "Usage:\n\tGenerate List:\n"
			"\t\tTexturePacker.exe -l output.list\n"
			"\tProcess List:\n"
			"\t\tTexturePacker.exe input.list output.td output.png\n";

	if(argc < 2)
	{
		printf(usage);
		return 1;
	}

	if(argc == 3 && strcmp(argv[1], "-l") == 0)
	{
		std::vector<std::string> names;
		GetFileNames("", names);
		FILE *file = fopen(argv[2], "w");
		for(int i=0; i<(int)names.size(); i++)
			fprintf(file, "%s\n", names[i].data());
		fclose(file);
	}
	else if(argc == 4)
	{
		FILE *file = fopen(argv[1], "r");
		if(file == NULL)
		{
			printf("Couldn't open %s\n.", argv[1]);
			return 1;
		}

		char fileName[512];

		std::vector<ImageData> imageList;
		ImageData imageData;

		while(fgets(fileName, 512, file))
		{
			if(fileName[strlen(fileName) - 1] == '\n')
				fileName[strlen(fileName) - 1] = '\0';

			corona::Image *image = corona::OpenImage(fileName, corona::FF_AUTODETECT, corona::PF_B8G8R8A8);

			if(image == NULL)
			{
				printf("Couldn't open image %s\n", fileName);
				return 1;
			}

			imageData.image = image;
			imageData.name = fileName;
			imageData.originX = 0.5f;
			imageData.originY = 0.5f;

			imageList.push_back(imageData);
		}

		// todo: prune whitespace

		fge::TexturePacker texturePacker(1);

		for(int i=0; i<imageList.size(); i++)
		{
			texturePacker.AddTexture(imageList[i].image->getWidth(), imageList[i].image->getHeight());
		}

		int textureWidth, textureHeight;
		texturePacker.PackTextures(textureWidth, textureHeight);

		unsigned char *pixels = new unsigned char[textureWidth * textureHeight * 4];
		memset(pixels, 0, textureWidth * textureHeight * 4);

		TiXmlDocument xmlDocument;

		TiXmlElement rootElement("TextureDictionary");

		for(int i=0; i<imageList.size(); i++)
		{
			int x, y, width, height;

			texturePacker.GetDataByIndex(i, x, y, width, height);

			unsigned int *sourcePixels = (unsigned int*)imageList[i].image->getPixels();
			unsigned int *destinationPixels = (unsigned int*)pixels;

			for(int yPixel = 0; yPixel < height; yPixel++)
			{
				for(int xPixel = 0; xPixel < width; xPixel++)
				{
					destinationPixels[(yPixel + y) * textureWidth + xPixel + x] = sourcePixels[yPixel * width + xPixel];
				}
			}

			char temp[256];

			TiXmlElement imageElement("Image");
			imageElement.SetAttribute("name", imageList[i].name);

			TiXmlElement xElement("x");
			sprintf(temp, "%d", x);
			xElement.InsertEndChild(TiXmlText(temp));
			imageElement.InsertEndChild(xElement);

			TiXmlElement yElement("y");
			sprintf(temp, "%d", y);
			yElement.InsertEndChild(TiXmlText(temp));
			imageElement.InsertEndChild(yElement);

			TiXmlElement widthElement("width");
			sprintf(temp, "%d", width);
			widthElement.InsertEndChild(TiXmlText(temp));
			imageElement.InsertEndChild(widthElement);

			TiXmlElement heightElement("height");
			sprintf(temp, "%d", height);
			heightElement.InsertEndChild(TiXmlText(temp));
			imageElement.InsertEndChild(heightElement);

			TiXmlElement xOriginElement("xorigin");
			sprintf(temp, "%f", imageList[i].originX);
			xOriginElement.InsertEndChild(TiXmlText(temp));
			imageElement.InsertEndChild(xOriginElement);

			TiXmlElement yOriginElement("yorigin");
			sprintf(temp, "%f", imageList[i].originY);
			yOriginElement.InsertEndChild(TiXmlText(temp));
			imageElement.InsertEndChild(yOriginElement);

			rootElement.InsertEndChild(imageElement);
		}

		corona::Image *packedImage = corona::CreateImage(textureWidth, textureHeight, corona::PF_B8G8R8A8, pixels);

		xmlDocument.InsertEndChild(rootElement);
		xmlDocument.SaveFile(argv[2]);
		corona::SaveImage(argv[3], corona::FF_PNG, packedImage);

		delete pixels;
		delete packedImage;
		for(int i=0; i<imageList.size(); i++)
		{
			delete imageList[i].image;
		}
	}
	else
	{
		printf("Error processing input\n\n%s", usage);
		return 1;
	}

	return 0;
}

void GetFileNames(const std::string &searchPath, std::vector<std::string> &names)
{
	WIN32_FIND_DATAA findFileData;
	HANDLE hFind;

	std::string path = searchPath;
	if(searchPath != "" && path[path.size() - 1] != '/' && path[path.size() - 1] != '\\')
		path += "/";

	hFind = FindFirstFileA((path + "*").data(), &findFileData);
	do
	{
		if(findFileData.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)
		{
			if(findFileData.cFileName[0] == '.')
				continue;
			else
			{
				GetFileNames(path + findFileData.cFileName, names);
			}
		}
		else
		{
			std::string filename = findFileData.cFileName;
			if(filename.length() > 4 && filename.substr(filename.length() - 4) == ".png")
			{
				names.push_back(path + findFileData.cFileName);
			}
		}
	} while(FindNextFileA(hFind, &findFileData) != 0);
	FindClose(hFind);
}